PATH
MacOS X Server Release Notes Copyright © 1998 by Apple Computer, Inc. All Rights Reserved.
This release note provides some guidelines for writing a threaded application. For a related discussion, see Thread-safe Classes in Foundation in the release notes for the Foundation framework.
[NSApplication detachDrawingThread:]
to create a thread with its own graphics context).lockFocusIfCanDraw
(see "NSView," below) and unlockFocus
.[NSApplication
run]
calling [NSApplication
nextEventMatchingMask: ...]
and [NSApplication
sendEvent:]
. While the Application Kit continues
to work if other threads are involved in the event path,
operations can occur out of sequence. For example if two
different threads are responding to key events, the keys
could be received out of order. By letting the main
thread process events, you achieve a more consistent user
experience. The main thread can send a message (DO, Mach
message, or through a NSConditionLock) to get another
thread to do some work.Note: The Java Yellow Box API implementation (including AWT) is multi-threaded.
The major reward from multi-threading your application is improved performance on a multi-processor system.
Only one thread can use an NSDPSContext at a time. A thread can use multiple NSDPSContexts during its lifetime, but if two threads access a single NSDPSContext at the same time, unpredictable results can occur.
Flushing still occurs automatically after every event in the
main thread of the application. However, there are times when you
need to explicitly flush when drawing on other threads. Use the flush:
and flushGraphics:
methods of NSDPSContext to flush
the stream buffer and window backing store, respectively.
One thread can create an image, draw it, pass it off to another thread, which can then draw it, and so forth. The underlying image cache is shared among all threads.
You can create, destroy, resize, move, and perform other
operations with NSViews from different threads. The system
ensures that all drawing is deferred while these operations occur
(through the lockFocusIfCanDraw
and unlockFocus
mentioned above). lockFocusIfCanDraw
ensures that lockFocus
and canDraw
methods are called atomically. Subthread
drawing, now, should look like the code fragment below.
if ([aView lockFocusIfCanDraw]) { /* Drawing operations */ [aView unlockFocus]; }
In a multi-threaded application, the main thread is still
responsible for redisplaying dirty views through the same process
as a single-threaded application. The drawRect:
method of every dirty view is called in the main thread. If the
drawing needs to be done on another thread, the drawRect:
method for the view should arrange for the secondary thread to do
the drawing and not do any drawing in drawRect:
.
If a secondary thread of an application wants to cause
portions of the view to be redrawn on the main thread, the normal
mechanisms work (setNeedsDisplay:
, setNeedsDisplayInRect:
,
and setViewsNeedDisplay:
).
Performance Note: The view system's gstates are per-thread.
Using gstates in a multi-threaded application improves drawing
performance in a way similar to a single-threaded application.
Please use the public method (gstate
) to get at the
gstate of the current thread.
The collection classes in Foundation (for example, NSMutableArray, NSMutableDictionary) are not thread-safe. You must lock around them to use them in a multi-threaded application:
Not Thread-Safe:
thread-1 thread-2
count = [array count]; for (i = 0; i < count; i++) { [array addObject:newObject]; if ([array objectAtIndex:i] == obj) { // ... do something ... } }
In this example, if thread-1 were preempted while it iterated over the array, the second thread, thread-2 , could be scheduled and add an object to the array. When the first thread, thread-1, ran again, it's count variable would be stale, and it may not find the desired object.
Thread-Safe :
thread-1 thread-2
[arrayLock lock]; count = [array count]; for (i = 0; i < count; i++) { if ([array objectAtIndex:i] == obj) { [array addObject:newObject]; // ... do something ... } } [arrayLock unlock];
This is the same example using locks around the array manipulation. The first thread will complete its entire iteration before the second thread can access the array.
Note: See Foundation release notes for more information on Foundation thread support.
Thread termination can cause some drawing problems. In general, the Application Kit doesn't know if your application is using threads to draw or to do similar operations. Before dismissing a window, you should ensure that your threads have stopped drawing.
Using locks within drawRect:
can cause deadlock
problems if they are held beyond the drawRect:
. It
is safe to lock and unlock NSLocks within a drawRect:
.
You can now create a window on a secondary thread. The
AppKit ensures that the data structures associated with a window
are deallocated on the main thread in order to avoid race
conditions. There is some possibility that window objects
may leak in an application that deals with a lot of windows
concurrently. There is also a theoretical limit on the
number of windows that can be displayed at the same time.
You can now call [NSApplication postEvent:atStart:] from a
secondary thread to post an event to the main thread's event
queue. Order is not guaranteed with respect to user input
events, however. The main thread of the application is
still responsible for handling events in the event queue.
An explicit call to [NSView display] on a secondary thread
will no longer cause recursive display on that thread. The
call to display will be treated as if it were a call to [NSView
setNeedsDisplay:YES], and the recursive display will be done on
the main thread.
You can now create a modal window on a secondary thread. The AppKit blocks the calling secondary thread while the main thread is running the modal loop.